<?php
declare (strict_types = 1);

namespace app\api\middleware;


use spzger\traits\response\JumpResponse;
use thans\jwt\exception\JWTException;
use thans\jwt\exception\TokenBlacklistException;
use thans\jwt\exception\TokenBlacklistGracePeriodException;
use thans\jwt\exception\TokenExpiredException;
use thans\jwt\middleware\JWTAuth;
use think\Config;

use think\exception\HttpException;
use Exception;
use think\exception\HttpResponseException;

/**
 * JWT验证刷新token机制
 * Class JWTToken
 * @package app\api\middleware
 */
class JWT extends JWTAuth
{
    use JumpResponse;

    protected $cookieDomain;

    protected $header = [
        'Access-Control-Allow-Credentials' => 'true',
        'Access-Control-Max-Age'           => 1800,
        'Access-Control-Allow-Methods'     => 'GET, POST',
        'Access-Control-Allow-Headers'     => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With',
        'Access-Control-Expose-Headers'    => 'Authorization',
        'Access-Control-Allow-Origin'      => '*'
    ];

   /* public function __construct(Config $config)
    {
        $this->cookieDomain = $config->get('cookie.domain', '');
    }*/

    /**
     * 刷新token
     * @param $request
     * @param \Closure $next
     * @return mixed
     * @throws JWTException
     * @throws TokenBlacklistException
     * @throws TokenBlacklistGracePeriodException
     */
    public function handle($request, \Closure $next)
    {

        /**
         * 跨域
         */
        $header = !empty($header) ? array_merge($this->header, $header) : $this->header;

        if (!isset($header['Access-Control-Allow-Origin'])) {
            $origin = $request->header('origin');

            if ($origin && ('' == $this->cookieDomain || strpos($origin, $this->cookieDomain))) {
                $header['Access-Control-Allow-Origin'] = $origin;
            } else {
                $header['Access-Control-Allow-Origin'] = '*';
            }
        }

        try {
            $payload = $this->auth->auth();
        } catch (TokenExpiredException $e) { // 捕获token过期
            // 尝试刷新token，会将旧token加入黑名单
            try {
                $this->auth->setRefresh();
                $token = $this->auth->refresh();
                $payload = $this->auth->auth(false);
            } catch (TokenBlacklistGracePeriodException $e) {
                $payload = $this->auth->auth(false);
            } catch (JWTException $exception) {
                // 如果捕获到此异常，即代表 refresh 也过期了，用户无法刷新令牌，需要重新登录。

                throw new HttpException(401, $exception->getMessage(),null,$header);
//                $response = json($exception->getMessage());
//                throw new HttpResponseException($response);
            }
        } catch (TokenBlacklistGracePeriodException $e) { // 捕获黑名单宽限期
            $payload = $this->auth->auth(false);
        } catch (TokenBlacklistException $e) { // 捕获黑名单，退出登录或者已经自动刷新，当前token就会被拉黑
            throw new HttpException(401, '未登录..', null,$header);
        }

        // 可以获取payload里自定义的字段，比如uid
        $request->uid = $payload['uid']->getValue();

        $response = $next($request);
        // 如果有新的token，则在响应头返回（前端判断一下响应中是否有 token，如果有就直接使用此 token 替换掉本地的 token，以此达到无痛刷新token效果）
        if (isset($token)) {
            $response = $this->setAuthentication($response, $token,$header);
        }

        return $response;
    }
}
